/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2008 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id: header 252479 2008-02-07 19:39:50Z iliaa $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_scheme.h"

/* Declare module globals */
ZEND_DECLARE_MODULE_GLOBALS(scheme)

/* True global resources - no need for thread safety here */
static int le_scheme;

/* {{{ scheme_functions[]
 *
 * Every user visible function must have an entry in scheme_functions[].
 */
const zend_function_entry scheme_functions[] = {
	PHP_ME(scheme, version, NULL, ZEND_ACC_PUBLIC)
	PHP_ME(scheme, eval, NULL, ZEND_ACC_PUBLIC)
	{NULL, NULL, NULL}
};
/* }}} */

/* {{{ scheme_module_entry
 */
zend_module_entry scheme_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
	STANDARD_MODULE_HEADER,
#endif
	"scheme",
	scheme_functions,
	PHP_MINIT(scheme),
	PHP_MSHUTDOWN(scheme),
	PHP_RINIT(scheme),		/* Replace with NULL if there's nothing to do at request start */
	PHP_RSHUTDOWN(scheme),	/* Replace with NULL if there's nothing to do at request end */
	PHP_MINFO(scheme),
#if ZEND_MODULE_API_NO >= 20010901
	"0.1", /* Replace with version number for your extension */
#endif
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_SCHEME
ZEND_GET_MODULE(scheme)
#endif

/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("scheme.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_scheme_globals, scheme_globals)
    STD_PHP_INI_ENTRY("scheme.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_scheme_globals, scheme_globals)
PHP_INI_END()
*/
/* }}} */

/* {{{ php_scheme_init_globals
 */
/* Uncomment this function if you have INI entries
static void php_scheme_init_globals(zend_scheme_globals *scheme_globals)
{
	scheme_globals->global_value = 0;
	scheme_globals->global_string = NULL;
}
*/
/* }}} */

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(scheme)
{
	zend_class_entry ce;
	INIT_CLASS_ENTRY(ce, "Scheme", scheme_functions);
	scheme_ce = zend_register_internal_class(&ce TSRMLS_CC);

	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(scheme)
{
	/* uncomment this line if you have INI entries
	UNREGISTER_INI_ENTRIES();
	*/
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(scheme)
{
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
 */
PHP_RSHUTDOWN_FUNCTION(scheme)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(scheme)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "scheme support", "enabled");
	php_info_print_table_end();

	/* Remove comments if you have entries in php.ini
	DISPLAY_INI_ENTRIES();
	*/
}
/* }}} */

/* {{{ API string version(void)
 * Return the version string. */
PHP_METHOD(scheme, version)
{
	int len;
	char *strg;
	len = spprintf(&strg, 0, "php-scheme %s (with %s)",
		PHP_SCHEME_VERSION, SCHEME_LIB_VERSION);
	RETURN_STRINGL(strg, len, 0);
}
/* }}} */

/* {{{ Custom scheme_init() function
 */
#undef USE_CUSTOM_ALLOC
#ifdef USE_CUSTOM_ALLOC
static void* my_emalloc(size_t size) { return emalloc(size); }
static void my_efree(void *ptr) { return efree(ptr); }
#endif

static scheme* php_scheme_init(TSRMLS_D) {
	scheme *sc = SCHEME_G(scheme);
#ifdef USE_CUSTOM_ALLOC
	sc = scheme_init_new_custom_alloc(my_emalloc, my_efree);
#else
	sc = scheme_init_new();
#endif
	if (sc != NULL) {
		SCHEME_G(scheme) = sc;

		// FIXME 
		FILE *fp = fopen("tinyscheme/init.scm", "r");
		scheme_load_file(sc, fp);
		fclose(fp);

    	scheme_set_input_port_file(sc, stdin);
    	scheme_set_output_port_file(sc, stdout);
		return sc;
	}

	return NULL;
}
/* }}} */

/* {{{ API mixed eval(string s)
 * Return the result of eval. */
PHP_METHOD(scheme, eval)
{
    scheme *sc;
    char *expression;
	int expression_len;

    pointer l;
    char *p;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &expression, &expression_len) == FAILURE) {
		RETURN_FALSE;
    }

	// Initialize scheme interpreter.
    sc = php_scheme_init(TSRMLS_C);
	if (sc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Scheme interpreter init failure");
		RETURN_FALSE;
	}

    scheme_load_string(sc, expression);

    // see scheme.c - atom2str()
    l = sc->value;
    if (l == sc->NIL) {
        //p = "()";
		ZVAL_NULL(return_value);
        Z_TYPE_P(return_value) = IS_NULL;

    } else if (l == sc->T) {
        //p = "#t";
		ZVAL_BOOL(return_value, 1);
		Z_TYPE_P(return_value) = IS_BOOL;

    } else if (l == sc->F) {
        //p = "#f";
		ZVAL_BOOL(return_value, 0);
		Z_TYPE_P(return_value) = IS_BOOL;

    }  else if (is_number(l)) {
        #define ivalue_unchecked(p)       ((p)->_object._number.value.ivalue)
        #define rvalue_unchecked(p)       ((p)->_object._number.value.rvalue)
        if(is_integer(l)) {
            Z_LVAL_P(return_value) = ivalue_unchecked(l);
            Z_TYPE_P(return_value) = IS_LONG;
        } else {
            if ((double)LONG_MIN <= rvalue_unchecked(l) &&
                rvalue_unchecked(l) <= (double)LONG_MAX) {
                convert_to_double(return_value);
                Z_DVAL_P(return_value) = rvalue_unchecked(l);
                Z_TYPE_P(return_value) = IS_DOUBLE;
            }
        }

    } else if (is_string(l)) {
		#define strvalue(p)      ((p)->_object._string._svalue)
		#define strlength(p)     ((p)->_object._string._length)
		Z_STRVAL_P(return_value) = (char*)estrdup(strvalue(l));
		Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
		Z_TYPE_P(return_value) = IS_STRING;

	}else if (is_character(l)) {
    	int c=charvalue(l);
        p = sc->strbuff;
		p[0]=c;
        p[1]=0;
		Z_STRVAL_P(return_value) = (char*)estrdup(p);
		Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
		Z_TYPE_P(return_value) = IS_STRING;

	} else if (is_symbol(l)) {
          p = symname(l);
		  // TODO
	}

	// TODO
	// - string-list

    scheme_deinit(sc);
}

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
